package org.openwebtop.common.aop.aspect;

import org.apache.commons.lang.ClassUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.openwebtop.common.aop.annotation.RetryOnException;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

/**
 * Retry Aspect
 *
 * @author Jaehyeon Nam
 * @Since 2010. 1. 11.
 */
@Aspect
@Order(2)
@Component
public class RetryOnExceptionAspect {
	/**
	 * Logger
	 */
	private final static Log LOG = LogFactory.getLog(RetryOnExceptionAspect.class);

	/**
	 * Retry Aspect Method
	 *
	 * @param joinPoint JoinPoint
	 * @return JoinPoint Method Invoke Result
	 * @throws Throwable Throwable
	 */
	@Around("@annotation(org.openwebtop.common.aop.annotation.RetryOnException)")
	public Object retry(ProceedingJoinPoint joinPoint) throws Throwable {
		final MethodSignature signature = (MethodSignature)joinPoint.getSignature();
		final RetryOnException retryOnExceptionAnnotation = signature.getMethod().getAnnotation(RetryOnException.class);

		if (retryOnExceptionAnnotation != null) {
			int numAttempts = 0;
			Throwable throwable;

			do {
				numAttempts++;

				try {
					return joinPoint.proceed();
				} catch (Throwable t) {
					throwable = t;

					if (ClassUtils.isAssignable(t.getClass(), retryOnExceptionAnnotation.exception()) == false) {
						break;
					}

					if (LOG.isDebugEnabled()) {
						LOG.debug(String.format("Exception[%s] has been occured! Retry %d/%d times", ClassUtils.getShortCanonicalName(t.getClass()), numAttempts, retryOnExceptionAnnotation.count()));
					}
				}
			} while (numAttempts < retryOnExceptionAnnotation.count());

			throw throwable;
		}

		return joinPoint.proceed();
	}

}
